package com.company.service.impl;

import com.company.CompanyException;
import com.company.common.Constants;
import com.company.common.PageUtils;
import com.company.model.Comment;
import com.company.model.CommentType;
import com.company.model.request.CommentForm;
import com.company.model.request.CommentQuery;
import com.company.model.result.PageResult;
import com.company.repository.CommentRepository;
import com.company.service.ICacheService;
import com.company.service.ICommentService;
import com.company.service.ICompanyService;
import com.company.utils.JsonUtil;
import com.google.common.collect.Lists;
import com.google.gson.reflect.TypeToken;
import java.util.Date;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

/**
 * Created by tu on 2017/4/27.
 */
@Service
public class CommentServiceImpl implements ICommentService {

  private static final Logger logger = LoggerFactory.getLogger(CommentServiceImpl.class);

  @Autowired
  CommentRepository repository;
  @Autowired
  ICompanyService companyService;

  @Autowired
  ICacheService cacheService;

  @Override
  public PageResult<Comment> list(CommentQuery query) {
    PageResult<Comment> comments;

    // FIXME: 26/07/2017 写死分页参数为500
    if ((null == query.getPageSize()) || (0 == query.getPageSize())) {
      query.setPageSize(500);
    }

    final String key =
        Constants.REDIS_KEY_COMMENTS_PREFIX + query.getCompanyId() + ":" + query.toString();
    Boolean exist = cacheService.exists(key);
    if (exist) {
      String value = cacheService.get(key);
      comments = JsonUtil.fromJson(value, new TypeToken<PageResult<Comment>>() {
      });
    } else {
      Specification<Comment> specification = initSpecification(query);
      PageRequest pageRequest = PageUtils.initPageRequest(query);
      logger
          .info("开始调用repository.findAll,参数{},{}", specification.toString(),
              pageRequest.toString());
      Page<Comment> page = repository
          .findAll(specification, pageRequest);
      logger.info("结束调用repository.findAll,参数{},{},结果{}",
          specification.toString(),
          pageRequest.toString(), page.toString());

      List<Comment> content = page.getContent();
      if (!ObjectUtils.isEmpty(content)) {
        for (int i = 0; i < content.size(); i++
            ) {
          Comment comment = content.get(i);

          CommentQuery childQuery = new CommentQuery();
          childQuery.setCompanyId(comment.getTargetId());
          childQuery.setParentId(comment.getId());
          //2017/7/18 查询2000条子评论
          childQuery.setPageSize(2000);

          Specification<Comment> specificationChild = initSpecification(childQuery);
          PageRequest pageRequestChild = PageUtils.initPageRequest(childQuery);
          logger
              .info("开始调用repository.findAll,参数{},{}", specificationChild.toString(),
                  pageRequestChild.toString());
          Page<Comment> pageChild = repository
              .findAll(specificationChild, pageRequestChild);
          logger.info("结束调用repository.findAll,参数{},{},结果{}",
              specificationChild.toString(),
              pageRequestChild.toString(), pageChild.toString());

          content.get(i).setChildsByComment(pageChild.getContent());
        }
      }

      comments = PageUtils.wrapPageResult(page);
      comments.setContent(content);

      cacheService.set(key, JsonUtil.toJson(comments));
    }
    return comments;
  }

  @Override
  public List<Comment> list(Long companyId, String content, String ip) {
    logger
        .info("开始调用repository.findComments,参数companyId={},content={},ip={}", companyId, content,
            ip);
    List<Comment> comments = repository.findComments(companyId, content, ip);
    logger.info("结束调用repository.findComments,参数companyId={},content={},ip={},结果{}", companyId,
        content, ip,
        comments.toString());
    return comments;
  }

  @Override
  public Comment find(Long id) {
    logger.info("开始调用repository.findOne,param:{}", id);
    Comment comment = repository.findOne(id);
    logger.info("结束调用repository.findOne,param:{},result:{}", id, comment.toString());
    return comment;
  }

  @Override
  @Transactional
  public Comment add(CommentForm info, String userAgent) throws CompanyException {
    if (info.getContent().trim().length() == 0) {
      throw new CompanyException("姿势错误,正确选择评论");
    }
    List<Comment> comments = list(info.getCompanyId(), info.getContent(), userAgent);
    if (!ObjectUtils.isEmpty(comments)) {
      throw new CompanyException("不要以同一姿势重复插入,来点新鲜的");
    }

    if (ObjectUtils.isEmpty(info.getParentId()) || (info.getParentId().compareTo(0l) == 0)) {
      CommentType type =
          (null == info.getType()) ? CommentType.BURY : CommentType.fromType(info.getType());
      info.setType(type.type);
    } else {
      //2017/7/18 判断 company id,parentid是否对应
      Comment parent = repository.findOne(info.getParentId());
      if (ObjectUtils.isEmpty(parent)) {
        throw new CompanyException("姿势错误,正确选择评论");
      }
      parent.setUpdateTime(new Date());
      logger
          .info("开始调用repository.save,参数{}", parent.toString());
      parent = repository.save(parent);
      logger
          .info("结束调用repository.save,参数{},结果{}", parent.toString(), parent.toString());
    }

    Comment comment = new Comment(info, userAgent);
    logger
        .info("开始调用repository.save,参数{}", comment.toString());
    Comment savedInfo = repository.save(comment);
    logger
        .info("结束调用repository.save,参数{},结果{}", comment.toString(), savedInfo.toString());

    //清除缓存
    cacheService.delPattern(Constants.REDIS_KEY_COMMENTS_PREFIX + info.getCompanyId() + ":" + "*");
    cacheService.delPattern(Constants.REDIS_KEY_COMPANIES_PREFIX + "*");
    return savedInfo;
  }

  private Specification<Comment> initSpecification(final CommentQuery commentQuery) {
    return new Specification<Comment>() {
      @Override
      public Predicate toPredicate(Root<Comment> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
        Predicate predicate = cb.conjunction();
        List<Expression<Boolean>> expressions = predicate.getExpressions();
        List<Order> orders = Lists.newArrayList();
        if (!ObjectUtils.isEmpty(commentQuery.getCompanyId())) {
          expressions.add(cb.equal(root.<String>get("targetId"), commentQuery.getCompanyId()));
        }

        if (!ObjectUtils.isEmpty(commentQuery.getParentId()) && (
            commentQuery.getParentId().compareTo(0l) != 0)) {
          //子评论
          expressions.add(cb.equal(root.<String>get("parentId"), commentQuery.getParentId()));
          orders.add(cb.asc(root.<String>get("createTime")));
        } else {
          //一级评论，根据更新时间倒序
          orders.add(cb.desc(root.<String>get("updateTime")));
          expressions.add(cb.equal(root.<String>get("parentId"), 0));
        }

        query.orderBy(orders);
        expressions.add(cb.equal(root.<String>get("deleted"), 0));
        return predicate;
      }
    };
  }
}